package com.smarttiger.zero.admin.webservice;

import com.smarttiger.zero.admin.dao.*;
import com.smarttiger.zero.admin.entity.ArgsModel;
import com.smarttiger.zero.admin.entity.InterfaceArgsModel;
import com.smarttiger.zero.admin.entity.InterfaceModel;
import com.smarttiger.zero.admin.entity.LogicModel;
import javassist.*;
import javassist.bytecode.ClassFile;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;

import java.util.*;


@Configuration
public class WebServiceConfig implements ApplicationListener<ContextRefreshedEvent> {

    Map<String,EndpointImpl> endpointMap;
    SpringBus springBus;

    @Value("${ws.locationUrl}")
    String locationUrl;

    @Autowired
    InterfaceDao interfaceDao;

    @Autowired
    InterfaceLogicDao interfaceLogicDao;

    @Autowired
    InterfaceArgsDao interfaceArgsDao;

    @Autowired
    LogicDao logicDao;

    @Autowired
    ArgsDao argsDao;

    @Bean
    public ServletRegistrationBean disServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/services/*");
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        springBus = new SpringBus();
        return springBus;
    }

    @Bean(name="endpointList")
    public Map<String,EndpointImpl> endpoint() {
        endpointMap = new HashMap<>();
        return endpointMap;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        try {
            List<InterfaceModel> ins = interfaceDao.findAll();
            for (InterfaceModel in : ins) {
                if (publishWS(in)) continue;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean publishWS(InterfaceModel in) throws Exception {
        List<InterfaceArgsModel> inAgrs = interfaceArgsDao.findAllByInterfaceId(in.getId());
        List<LogicModel> logicModel = logicDao.findLogicModelByInterfaceId(in.getId());
        if(logicModel.size()==0) return true;
        String method = "";
        LogicModel logicModel1 = logicModel.get(0);
        if("com.smarttiger.zero.logic.impl.WebServiceLogic".equals(logicModel1.getClassName())){
            List<ArgsModel> as = argsDao.findAllByLogicId(logicModel1.getId());
            Optional<ArgsModel> first = as.stream().filter(e -> "method".equals(e.getName())).findFirst();
            if(first.isPresent()){
                method = first.get().getDefaultValue();
            }
        }else {
            method = "wsApi";
        }

        String code = in.getCode();
        String wsdlKey = in.getCode();

        String resultType = "java.lang.String";

        Class aClass = getImplementor(code, method, wsdlKey,resultType,inAgrs);
        EndpointImpl endpoint = new EndpointImpl(springBus(), aClass.newInstance());
        endpoint.setPublishedEndpointUrl(locationUrl+wsdlKey+"?wsdl");
        endpoint.publish("/"+wsdlKey);

        in.setWsStatus("R");
        interfaceDao.save(in);

        endpointMap.put(wsdlKey,endpoint);
        return false;
    }

    public void stopWS(InterfaceModel in){
        if(endpointMap.containsKey(in.getCode())){
            EndpointImpl endpoint = endpointMap.get(in.getCode());
            endpoint.getServer().stop();
            endpoint.getServer().destroy();
            endpointMap.remove(in.getCode());
            in.setWsStatus("S");
            interfaceDao.save(in);
        }
    }

    public Class getImplementor(String code,String method,String wsdlKey,String resultType,List<InterfaceArgsModel> inAgrs ) throws Exception{

        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(this.getClass()));
        CtClass cc = pool.get("com.smarttiger.zero.admin.webservice.AppService");
        cc.setName("com.smarttiger.zero.admin.webservice.AppService"+code+(System.currentTimeMillis()));

        CtClass rType = pool.get(resultType);
        CtClass[] ppType = new CtClass[inAgrs.size()+1];

        String _$s = "";
        for (int i = 0; i < ppType.length; i++) {
            if(i==0){
                ppType[i] = pool.get("java.lang.String");
            }else {
                ppType[i] = pool.get(inAgrs.get(i-1).getTypecode());
            }
            _$s += (",$"+(i+1));
        }

        CtMethod ctMethod = new CtMethod(rType, method, ppType, cc);
        ctMethod.setModifiers(Modifier.PUBLIC);
        StringBuffer body = new StringBuffer();
        body.append("{");
        body.append("\n    code = \""+code+"\";     ");
        body.append("\n    Object[] os = {code"+_$s+"};    ");
        body.append("\n    return $proceed(os);  ");
        body.append("\n}");
        ctMethod.setBody(body.toString(),"this","execute");
        cc.addMethod(ctMethod);

        ClassFile ccFile = cc.getClassFile();
        ConstPool constPool = ccFile.getConstPool();

        // 添加方法注解
        AnnotationsAttribute methodAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation methodAnnot = new Annotation("javax.jws.WebMethod", constPool);
        methodAnnot.addMemberValue("operationName", new StringMemberValue(method, constPool));
        methodAttr.addAnnotation(methodAnnot);

        // 添加返回值注解
        Annotation resultAnnot = new Annotation("javax.jws.WebResult", constPool);
        resultAnnot.addMemberValue("name", new StringMemberValue("result", constPool));
        methodAttr.addAnnotation(resultAnnot);

        ctMethod.getMethodInfo().addAttribute(methodAttr);

        // 添加参数注解
        ParameterAnnotationsAttribute parameterAtrribute = new ParameterAnnotationsAttribute(
                constPool, ParameterAnnotationsAttribute.visibleTag);
        Annotation[][] paramArrays = new Annotation[inAgrs.size()+1][1];
        Annotation tokenAnnot = new Annotation("javax.jws.WebParam", constPool);
        tokenAnnot.addMemberValue("name", new StringMemberValue("token", constPool));
        paramArrays[0][0] = tokenAnnot;

        for (int i = 0; i < inAgrs.size(); i++) {
            InterfaceArgsModel inAgr = inAgrs.get(i);
            Annotation paramAnnot = new Annotation("javax.jws.WebParam", constPool);
            paramAnnot.addMemberValue("name", new StringMemberValue(inAgr.getName(), constPool));
            paramArrays[i+1][0] = paramAnnot;
        }
        parameterAtrribute.setAnnotations(paramArrays);

        ctMethod.getMethodInfo().addAttribute(parameterAtrribute);

        Class aClass = cc.toClass();
        return aClass;
    }


}
